﻿// server.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cstdio>
#include <tchar.h>
#include <atlstr.h> // use CString
#include "global.h"

#define MAX_BUFFER_SIZE 512

using namespace std;
using namespace ted;


int _tmain(int argc, _TCHAR* argv[]) {
	SocketHandler* handler = new SocketHandler();
	EventSelectServer server(handler,_T("127.0.0.1"),8080);
	bool success = server.Bind();

	while(true) {
		Sleep(1000);
	}
}


///////////////////////////////////////////////////////////////////////////////
// 소켓 정보 저장을 위한 구조체
typedef struct {
	SOCKET sock;
	char recvBuffer[MAX_BUFFER_SIZE + 1];
	char sendBuffer[MAX_BUFFER_SIZE + 1];
	int recvBytes;
	int sendBytes;

} SOCKET_INFO;

int g_nTotalSockets = 0;

SOCKET_INFO* g_aSocketInfoArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT g_aEventArray[WSA_MAXIMUM_WAIT_EVENTS];

// 소켓 관리 함수
BOOL AddSocketInfo(SOCKET clientSocket);
void RemoveSocketInfo(int nIndex);

// 오류 출력 함수
void err_quit(TCHAR* msg);
void err_display(TCHAR* msg);
void err_display(int errCode);


void Server2()
{
	cout<<"Start Server"<<endl;
	int retValue;

	// 윈속 초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
		return;

	// socket()
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (listenSocket == INVALID_SOCKET)
		err_quit(_T("socket()"));

	//소켓정보 추가
	if (AddSocketInfo(listenSocket) == FALSE)
		return;

	// WSAEventSelect 설정
	retValue = WSAEventSelect(listenSocket, g_aEventArray[g_nTotalSockets - 1], FD_ACCEPT | FD_CLOSE);
	if (retValue == SOCKET_ERROR)
		err_quit(_T("WSAEventSelect()"));

	// bind()
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(8080);
//	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	retValue = bind(listenSocket, (SOCKADDR*) &serverAddr, sizeof(serverAddr));
	if (retValue == SOCKET_ERROR) {
		err_quit(_T("bind()"));
	}else{
		cout<<"bind server="<<inet_ntoa(serverAddr.sin_addr)<<":8080"<<endl;
	}

	//listen()
	retValue = listen(listenSocket, SOMAXCONN);
	if (retValue == SOCKET_ERROR)
		err_quit(_T("listen()"));

	// 넌블로킹 소켓으로 전환
	unsigned long on = TRUE;
	retValue = ioctlsocket(listenSocket, FIONBIO, &on);
	if (retValue == SOCKET_ERROR)
		err_display(_T("ioctlsocket()"));

	// 통신에 사용할 변수
	int index;
	WSANETWORKEVENTS networkEvents;
	SOCKET clientSocket;
	SOCKADDR_IN clientAddr;
	int nAddrLength;



	while (1) {
		index = WSAWaitForMultipleEvents(g_nTotalSockets, g_aEventArray, FALSE, WSA_INFINITE, FALSE);
		if (index == WSA_WAIT_FAILED ) {
			err_display(_T("WSAWaitForMultipleEvents()"));
			continue;
		}

		index -= WSA_WAIT_EVENT_0;

		// 구체적인 네트워크 이벤트 알아내기
		retValue = WSAEnumNetworkEvents(g_aSocketInfoArray[index]->sock, g_aEventArray[index], &networkEvents);
		if (retValue == SOCKET_ERROR) {
			err_display(_T("WSAEnumNetworkEvents()"));
			continue;
		}

		// FD_ACCEPT 이벤트 처리
		if (networkEvents.lNetworkEvents & FD_ACCEPT) {
			if (networkEvents.iErrorCode[FD_ACCEPT_BIT] != 0) {
				err_display(networkEvents.iErrorCode[FD_ACCEPT_BIT]);
				continue;
			}
			nAddrLength = sizeof(clientAddr);
			clientSocket = accept(g_aSocketInfoArray[index]->sock, (SOCKADDR*) &clientAddr, &nAddrLength);
			if (clientSocket == INVALID_SOCKET) {
				err_display(_T("accept()"));
				continue;
			}

			// inet_ntoa : address 에서 주소값(char*)으로 변환해 가져온다.(x.x.x.x)
			// ntohs : short 메모리 값을 네트워크 바이트 순서에서 호스트 바이트 순서로 변경 (빅엔디안, 리틀엔디안)
			fprintf(stderr, "[TCP 서버] 클라이언트 접속: IP 주소 = %s, 포트번호 = %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

			if (AddSocketInfo(clientSocket) == FALSE) {
				closesocket(clientSocket);
				continue;
			}

			retValue = WSAEventSelect(clientSocket, g_aEventArray[g_nTotalSockets - 1], FD_READ | FD_WRITE | FD_CLOSE);

			if (retValue == SOCKET_ERROR)
				err_quit(_T("WSAEventSelect()"));
		}

		// FD_READ, FD_WRITE 이벤트 처리
		if ((networkEvents.lNetworkEvents & FD_READ)||
		(networkEvents.lNetworkEvents & FD_WRITE)){
		if((networkEvents.lNetworkEvents & FD_READ) && (networkEvents.iErrorCode[FD_READ_BIT] != 0))
		{
			err_display(networkEvents.iErrorCode[FD_READ_BIT]);
			continue;
		}

		if((networkEvents.lNetworkEvents & FD_WRITE) && (networkEvents.iErrorCode[FD_WRITE_BIT] != 0))
		{
			err_display(networkEvents.iErrorCode[FD_WRITE_BIT]);
			continue;
		}

		SOCKET_INFO* pSocketInfo = g_aSocketInfoArray[index];

		if(pSocketInfo->recvBytes == 0)
		{
			// 데이터 받기
			retValue = recv(pSocketInfo->sock, pSocketInfo->recvBuffer, MAX_BUFFER_SIZE, 0);
			if(retValue == SOCKET_ERROR)
			{
				// WOULDBLOCK 이 발생되는 경우 (잠시 사용할수 없는상태)
				// 소켓이 제대로 초기화되지 않은 상태에서 사용하는경우나 소켓이 끊어졌는데 계속 사용할라고 하는경우,
				// 소켓에 데이터가 들어오지 않았는데 소켓으로부터 일정량을 읽을려고 하는 경우,
				// 소켓에 데이터가 들어왔지만 그 데이터보다 더 큰량의 데이터를 읽을려고 하는경우
				if(WSAGetLastError() != WSAEWOULDBLOCK)
				{
					err_display(_T("recv()"));
					RemoveSocketInfo(index);
				}
				continue;
			}
			pSocketInfo->recvBytes = retValue;
			pSocketInfo->recvBuffer[retValue] = '\0';
			nAddrLength = sizeof(clientAddr);
			getpeername(pSocketInfo->sock, (SOCKADDR*)&clientAddr, &nAddrLength);
			printf("[TCP/%s:%d] %s\n", inet_ntoa(clientAddr.sin_addr),
					ntohs(clientAddr.sin_port), pSocketInfo->recvBuffer);



			// 에코서버이므로 받은데이터를 반송한다.
			pSocketInfo->sendBytes = retValue;
			memcpy(pSocketInfo->sendBuffer, pSocketInfo->recvBuffer, retValue);

			IoBuffer* buf = new IoBuffer(512);
			buf->AddBytes((BYTE*)pSocketInfo->recvBuffer, retValue);
			buf->Flip();
			buf->ToHexDump();
			delete buf;

			// 보낸만큼 리시브버퍼를 관리한다.
			pSocketInfo->recvBytes -= pSocketInfo->sendBytes;
			memmove(pSocketInfo->recvBuffer, pSocketInfo->recvBuffer + retValue, pSocketInfo->recvBytes);
		}

		if(pSocketInfo->sendBytes > 0)
		{
			// 보내고자 하는 버퍼에 값을 모두 보낼 때 까지 처리한다.
			do
			{
				retValue = send(pSocketInfo->sock, pSocketInfo->sendBuffer, pSocketInfo->sendBytes, 0);
				if(retValue == SOCKET_ERROR)
				{
					if(WSAGetLastError() != WSAEWOULDBLOCK)
					{
						err_display(_T("send()"));
						RemoveSocketInfo(index);
					}
					continue;
				}
				pSocketInfo->sendBytes -= retValue;
				memmove(pSocketInfo->sendBuffer, pSocketInfo->sendBuffer + retValue, pSocketInfo->sendBytes);

			}while(pSocketInfo->sendBytes > 0);
		}
	}

		// 접속 종료
		if (networkEvents.lNetworkEvents & FD_CLOSE) {
			if (networkEvents.iErrorCode[FD_CLOSE_BIT] != 0)
				err_display(networkEvents.iErrorCode[FD_CLOSE_BIT]);

			RemoveSocketInfo(index);
		}
	}
	// 윈속 종료
	WSACleanup();
	return;
}

BOOL AddSocketInfo(SOCKET clientSocket) {
	if (g_nTotalSockets >= WSA_MAXIMUM_WAIT_EVENTS) {
		printf("[오류] 소켓 정보를 추가할 수 없습니다.\n");
		return FALSE;
	}

	SOCKET_INFO* pSocketInfo = new SOCKET_INFO;
	if (pSocketInfo == NULL) {
		printf("[오류] 메모리가 부족합니다.\n");
		return FALSE;
	}

	WSAEVENT hEvent = WSACreateEvent();
	if (hEvent == WSA_INVALID_EVENT ) {
		err_display(_T("WSACreateEvent()"));
		return FALSE;
	}

	pSocketInfo->sock = clientSocket;
	pSocketInfo->recvBytes = 0;
	pSocketInfo->sendBytes = 0;

	g_aSocketInfoArray[g_nTotalSockets] = pSocketInfo;
	g_aEventArray[g_nTotalSockets] = hEvent;
	g_nTotalSockets++;
}

void RemoveSocketInfo(int nIndex) {
	SOCKET_INFO* pInfo = g_aSocketInfoArray[nIndex];

	// 클라이언트 정보 얻기
	SOCKADDR_IN socketAddr;
	int nAddrLength = sizeof(socketAddr);
	getpeername(pInfo->sock, (SOCKADDR*) &socketAddr, &nAddrLength);
	printf("[TCP 서버] 클라이언트 종료: IP 주소 = %s, 포트번호 = %d\n", inet_ntoa(socketAddr.sin_addr), ntohs(socketAddr.sin_port));

	closesocket(pInfo->sock);
	delete pInfo;
	WSACloseEvent(g_aEventArray[nIndex]);

	for (int i = nIndex; i < g_nTotalSockets; ++i) {
		g_aSocketInfoArray[i] = g_aSocketInfoArray[i + 1];
		g_aEventArray[i] = g_aEventArray[i + 1];
	}

	g_nTotalSockets--;
}

// 주어진 에러코드에 대한 메세지를 가져온다.
static void GetMessage(INT32 errorCode, TCHAR* msg) {
	// MAKELANGID :usPrimaryLanguage, usSubLanguage를 통해 언어식별자를 WORD형으로 가져온다.
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL);
}

void log(int code, TCHAR* msg) {
	if(code==0) {
		cout<<msg<<endl;
		OutputDebugString(msg);
		return;
	}
	int errorCode =WSAGetLastError();
	TCHAR error_msg[1024] = { 0, };
	TCHAR content[2048] = { 0, };
	GetMessage(code, error_msg);

	_sntprintf(content, 2048, _T("%s error=%s"), msg,error_msg);

	cout<<content<<endl;
#ifdef _DEBUG
	//VS_CONSOLE(content);
	CString str;
	str.Format(_T("%s"),content);
	OutputDebugString(str);
#endif 	
}

void err_quit(TCHAR* msg) {
	log(WSAGetLastError(),msg);	
	exit(-1);
}

void err_display(TCHAR* msg) {
	log(WSAGetLastError(),msg);	
}

void err_display(int errCode) {
	log(errCode,_T(""));
}
